<!DOCTYPE html>
<html>
    <head>
        <title>dnd5ebuy</title>
        <meta charset="UTF-8" />
        <meta
            name="viewport"
            content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover"
        />
        <style>
            * {
                padding: 0;
                margin: 0;
            }
            .f-r {
                display: flex;
                flex-direction: row;
            }
            .f-c {
                display: flex;
                flex-direction: column;
            }
            .j-c {
                justify-content: center;
            }
            .j-s-b {
                justify-content: space-between;
            }
            .a-c {
                align-items: center;
            }
            .a-s-b {
                align-items: space-between;
            }
            input {
                width: 60px;
                text-align: center;
            }
            input.value {
                border: unset;
                background: unset;
                width: 30px;
            }
            .lable {
                padding: 0 5px;
                display: flex;
                align-items: center;
            }
            .lable > * {
                margin-right: 10px;
            }
            .lable > *:last-child {
                margin-right: 0;
            }
            #app {
                padding: 10px 20px;
            }
        </style>
        <script
            src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.4.33/vue.global.prod.min.js"
            integrity="sha512-J5rsLpdUqLDqN3r5s2cxH1eCp4DLSX4sNLhl3FGiI+10kFhO0Qb/LIul9eFowKe3H+J+b1jNjCUB0VTVjkv7pg=="
            crossorigin="anonymous"
            referrerpolicy="no-referrer"
        ></script>
    </head>
    <body>
        <div id="app">
            <div class="lable f-r" style="margin-bottom: 10px">
                <div>宽</div>
                <input v-model="input.width" />
                <div>高</div>
                <input v-model="input.height" />
                <div>密度%</div>
                <input v-model="input.rate" />
                <button @click="init">初始化</button>
            </div>
            <canvas
                id="canvas"
                :width="canvasWidth"
                :height="canvasHeight"
            ></canvas>
        </div>
        <script>
            const mapItem = {
                init: 0,
                number: 0,
                mine: 0,
                show: 0,
                image: 0,
            }
            const rand = (n) => {
                return Math.floor(Math.random() * n)
            }
            const vue = Vue.createApp({
                data() {
                    return {
                        input: {
                            width: 24,
                            height: 24,
                            rate: 15,
                        },
                        size: 20,
                        width: 0,
                        height: 0,
                        number: 0,
                        map: [[]],
                        found: {},
                        canvas: null,
                        ctx: null,
                    }
                },
                computed: {
                    canvasWidth() {
                        return this.map.length * this.size
                    },
                    canvasHeight() {
                        return this.map[0].length * this.size
                    },
                },
                methods: {
                    init() {
                        this.input.width = parseInt(this.input.width) || 0
                        this.input.height = parseInt(this.input.height) || 0
                        this.input.rate = parseFloat(this.input.rate) || 0
                        if (
                            this.input.width <= 0 ||
                            this.input.height <= 0 ||
                            this.input.rate <= 0 ||
                            this.input.rate >= 100
                        ) {
                            return
                        }
                        this.width = this.input.width
                        this.height = this.input.height
                        this.number = Math.round(
                            (this.width * this.height * this.input.rate) / 100
                        )
                        const map = []
                        for (let i = 0; i < this.width; i++) {
                            map[i] = []
                            for (let j = 0; j < this.height; j++) {
                                map[i][j] = Object.assign({}, mapItem)
                                map[i][j].init = 1
                            }
                        }
                        this.map = map
                        this.create(0, 0)
                        this.search(0, 0)
                        this.draw()
                    },
                    search(x, y) {
                        this.found = {}
                        x *= this.width
                        y *= this.height
                        let i, j, item
                        for (i = 0; i < this.width; i++) {
                            this._search(x + i, y)
                            this._search(x + i, y + this.height - 1)
                        }
                        for (j = 0; j < this.height; j++) {
                            this._search(x, y + j)
                            this._search(x + this.width - 1, y + j)
                        }
                    },
                    _search(x, y, z) {
                        const k = x + "_" + y
                        if (this.found[k]) {
                            return
                        }
                        this.found[k] = true
                        const item = this.getMap(x, y)
                        if (item && item.init) {
                            if (item.number > 2) {
                                if (!item.mine && !z) {
                                    item.show = 1
                                }
                            } else {
                                item.show = 1
                                this._search(x - 1, y)
                                this._search(x, y - 1)
                                this._search(x + 1, y)
                                this._search(x, y + 1)
                            }
                        }
                    },
                    expand(x, y) {},
                    create(x, y) {
                        x *= this.width
                        y *= this.height
                        let i, j, k
                        for (k = 0; k < this.number; k++) {
                            i = rand(this.width) + x
                            j = rand(this.height) + y
                            if (this.map[i][j].mine) {
                                k--
                            } else {
                                this.bury(i, j)
                            }
                        }
                    },
                    bury(x, y) {
                        this.map[x][y].mine = 1
                        for (let i = x - 1; i <= x + 1; i++) {
                            for (let j = y - 1; j <= y + 1; j++) {
                                this.getMap(i, j, (item) => {
                                    item.number += 1
                                })
                            }
                        }
                        for (let i = x - 1; i <= x + 1; i++) {
                            this.getMap(i, y, (item) => {
                                item.number += 1
                            })
                        }
                        for (let j = y - 1; j <= y + 1; j++) {
                            this.getMap(x, j, (item) => {
                                item.number += 1
                            })
                        }
                    },
                    getMap(x, y, f) {
                        if (
                            0 <= x &&
                            x < this.map.length &&
                            0 <= y &&
                            y < this.map[x].length
                        ) {
                            return f ? f(this.map[x][y]) : this.map[x][y]
                        }
                        return false
                    },
                    rect(x, y) {
                        x = x * this.size
                        y = y * this.size
                        return [
                            [x, y],
                            [x + this.size, y],
                            [x + this.size, y + this.size],
                            [x, y + this.size],
                        ]
                    },
                    console() {
                        console.clear()
                        const a = []
                        for (j = 0; j < this.map[0].length; j++) {
                            a[j] = ""
                        }
                        for (i = 0; i < this.map.length; i++) {
                            for (j = 0; j < this.map[i].length; j++) {
                                a[j] += this.map[i][j].mine
                                    ? "*"
                                    : this.map[i][j].number
                                    ? this.map[i][j].number
                                    : " "
                            }
                        }
                        for (j = 0; j < this.map[0].length; j++) {
                            console.log(a[j])
                        }
                    },
                    draw() {
                        this.$nextTick(() => {
                            this.canvas = document.getElementById("canvas")
                            this.ctx = canvas.getContext("2d")
                            this.drawRect(
                                [0, 0],
                                this.canvasWidth,
                                this.canvasHeight,
                                "#fff"
                            )
                            let i, j, r, s
                            for (i = 0; i < this.map.length; i++) {
                                for (j = 0; j < this.map[i].length; j++) {
                                    r = this.rect(i, j)
                                    if (
                                        this.map[i][j].show &&
                                        this.map[i][j].number <= 2
                                    ) {
                                        j == 0 &&
                                            this.drawLine(r[0], r[1], "#eee")
                                        this.drawLine(r[1], r[2], "#eee")
                                        this.drawLine(r[2], r[3], "#eee")
                                        i == 0 &&
                                            this.drawLine(r[3], r[0], "#eee")
                                    } else {
                                        this.drawRect(
                                            r[0],
                                            this.size,
                                            this.size
                                        )
                                    }
                                }
                            }
                        })
                    },
                    drawRect(p1, width, height, color) {
                        this.ctx.fillStyle = color || "#000000"
                        this.ctx.fillRect(p1[0], p1[1], width, height)
                    },
                    drawLine(p1, p2, color, lineWidth) {
                        this.ctx.strokeStyle = color || "#000000"
                        this.ctx.lineWidth = lineWidth || 1
                        this.ctx.beginPath()
                        this.ctx.moveTo(p1[0], p1[1])
                        this.ctx.lineTo(p2[0], p2[1])
                        this.ctx.stroke()
                    },
                },
            }).mount("#app")
        </script>
    </body>
</html>
